package cn.iocoder.yudao.module.worker.service.clerkapply;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.module.mp.api.user.MpUserApi;
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
import cn.iocoder.yudao.module.trade.api.config.TradeConfigApi;
import cn.iocoder.yudao.module.trade.api.config.dto.AddressRespDTO;
import cn.iocoder.yudao.module.trade.api.config.dto.TradeConfigRespDTO;
import cn.iocoder.yudao.module.worker.controller.admin.category.vo.CategoryPageReqVO;
import cn.iocoder.yudao.module.worker.controller.admin.category.vo.CategoryRespVO;
import cn.iocoder.yudao.module.worker.controller.admin.goods.vo.WorkerGoodsPageReqVO;
import cn.iocoder.yudao.module.worker.controller.app.category.vo.AppCategoryPageReqVO;
import cn.iocoder.yudao.module.worker.controller.app.clerkapply.vo.AppClerkApplyPageReqVO;
import cn.iocoder.yudao.module.worker.controller.app.clerkapply.vo.AppClerkApplySaveReqVO;
import cn.iocoder.yudao.module.worker.controller.app.goods.vo.AppWorkerGoodsPageReqVO;
import cn.iocoder.yudao.module.worker.dal.dataobject.category.CategoryDO;
import cn.iocoder.yudao.module.worker.dal.dataobject.clerk.ClerkDO;
import cn.iocoder.yudao.module.worker.dal.dataobject.clerklevel.ClerkLevelDO;
import cn.iocoder.yudao.module.worker.dal.dataobject.goods.WorkerGoodsDO;
import cn.iocoder.yudao.module.worker.dal.mysql.category.CategoryMapper;
import cn.iocoder.yudao.module.worker.dal.mysql.clerk.ClerkMapper;
import cn.iocoder.yudao.module.worker.dal.mysql.clerklevel.ClerkLevelMapper;
import cn.iocoder.yudao.module.worker.dal.mysql.goods.WorkerGoodsMapper;
import cn.iocoder.yudao.module.worker.enums.WorkerClerkApplyStatusEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.worker.controller.admin.clerkapply.vo.*;
import cn.iocoder.yudao.module.worker.dal.dataobject.clerkapply.ClerkApplyDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.worker.dal.mysql.clerkapply.ClerkApplyMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.pay.enums.MessageTemplateConstants.CLERK_APPLY_SUCCESS;
import static cn.iocoder.yudao.module.worker.enums.ErrorCodeConstants.*;

/**
 * 店员申请 Service 实现类
 *
 * @author Jerry
 */
@Service
@Validated
public class ClerkApplyServiceImpl implements ClerkApplyService {

    @Resource
    private ClerkApplyMapper clerkApplyMapper;

    @Resource
    private ClerkMapper clerkMapper;

    @Resource
    private WorkerGoodsMapper workerGoodsMapper;

    @Resource
    private ClerkLevelMapper clerkLevelMapper;

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    public SocialClientApi socialClientApi;

    @Resource
    private TradeConfigApi tradeConfigApi;

    @Resource
    private MpUserApi mpUserApi;

    @Override
    public Long createClerkApply(ClerkApplySaveReqVO createReqVO) {
        // 插入
        ClerkApplyDO clerkApply = BeanUtils.toBean(createReqVO, ClerkApplyDO.class);
        clerkApplyMapper.insert(clerkApply);
        // 返回
        return clerkApply.getId();
    }

    @Override
    public void updateClerkApply(ClerkApplySaveReqVO updateReqVO) {
        ClerkApplyDO clerkApplyDO = clerkApplyMapper.selectById(updateReqVO.getId());
        if(WorkerClerkApplyStatusEnum.ENABLE.getType().equals(clerkApplyDO.getStatus())){
            throw exception(WORKER_CLERK_APPLY_STATUS);
        }
        // 更新
        ClerkApplyDO updateObj = BeanUtils.toBean(updateReqVO, ClerkApplyDO.class);
        clerkApplyMapper.updateById(updateObj);
    }

    @Override
    public void deleteClerkApply(Long id) {
        // 校验存在
        validateClerkApplyExists(id);
        // 删除
        clerkApplyMapper.deleteById(id);
    }

    private void validateClerkApplyExists(Long id) {
        if (clerkApplyMapper.selectById(id) == null) {
            throw exception(CLERK_APPLY_NOT_EXISTS);
        }
    }

    @Override
    public ClerkApplyDO getClerkApply(Long id) {
        return clerkApplyMapper.selectById(id);
    }

    @Override
    public ClerkApplyDO getClerkApply(Long userId, Long id) {
        return clerkApplyMapper.getClerkApply(userId, id);
    }

    @Override
    public PageResult<ClerkApplyDO> getClerkApplyPage(ClerkApplyPageReqVO pageReqVO) {
        return clerkApplyMapper.selectPage(pageReqVO);
    }

    @Override
    public Long createClerkApply(AppClerkApplySaveReqVO createReqVO) {
        TradeConfigRespDTO config = tradeConfigApi.getTradeConfig();

        if(!config.getAuthEnabled() && config.getQrcodeEnabled()){
            if(!mpUserApi.getQrcode(getLoginUserId())){
                throw exception(USER_FANS_QRCODE);
            }
        }

        // 查询是否有待审核的
        List<ClerkApplyDO> clerkApplyList = clerkApplyMapper.getClerkApplyList(createReqVO.getUserId());
        if(!CollectionUtils.isEmpty(clerkApplyList)){
            throw exception(CLERK_APPLY_EXISTS);
        }
        // 插入
        ClerkApplyDO clerkApply = BeanUtils.toBean(createReqVO, ClerkApplyDO.class);
        // 设置默认店员等级
        ClerkLevelDO clerkLevelDO = clerkLevelMapper.getClerkLevel();
        if(clerkLevelDO == null){
            throw exception(WORKER_CLERK_LEVEL);
        }
        clerkApply.setWorkerClerkLevelId(clerkLevelDO.getId());
        clerkApplyMapper.insert(clerkApply);
        // 返回
        return clerkApply.getId();
    }

    @Override
    public void updateClerkApply(AppClerkApplySaveReqVO updateReqVO) {
        // 校验存在
        ClerkApplyDO clerkApplyDO = clerkApplyMapper.getClerkApply(updateReqVO.getUserId(), updateReqVO.getId());
        if(clerkApplyDO == null){
            throw exception(CLERK_APPLY_NOT_EXISTS);
        }
        // 更新
        ClerkApplyDO updateObj = BeanUtils.toBean(updateReqVO, ClerkApplyDO.class);
        updateObj.setStatus(WorkerClerkApplyStatusEnum.DISABLE.getType());
        clerkApplyMapper.updateById(updateObj);
    }

    @Override
    public PageResult<ClerkApplyDO> getClerkApplyPage(AppClerkApplyPageReqVO pageReqVO) {
        return clerkApplyMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateClerkApplyStatus(Long id, Integer status) {
        // 更新状态
        ClerkApplyDO clerkApplyDO = clerkApplyMapper.selectById(id);
        ClerkLevelDO clerkLevelDO = clerkLevelMapper.selectById(clerkApplyDO.getWorkerClerkLevelId());

        if(WorkerClerkApplyStatusEnum.ENABLE.getType().equals(status)){
            if(clerkApplyDO.getWorkerClerkId() == null){
                ClerkDO clerkDO = BeanUtils.toBean(clerkApplyDO, ClerkDO.class);
                clerkDO.setWorkerClerkApplyId(clerkApplyDO.getId());
                clerkDO.setWorkerClerkLevelId(clerkLevelDO.getId());
                clerkDO.setLevel(clerkLevelDO.getLevel());
                clerkDO.setStatus(true);
                clerkMapper.insert(clerkDO);
                clerkApplyDO.setWorkerClerkId(clerkDO.getId());

                // 构建并发送模版消息
                getSelf().sendApplyResultMessage(clerkApplyDO);

            }else{
                ClerkDO clerkDO = clerkMapper.selectById(clerkApplyDO.getWorkerClerkId());
                clerkDO.setNickname(clerkApplyDO.getNickname());
                clerkDO.setAge(clerkApplyDO.getAge());
                clerkDO.setAvatar(clerkApplyDO.getAvatar());
                clerkDO.setSex(clerkApplyDO.getSex());
                clerkDO.setAlbums(clerkApplyDO.getAlbums());
                clerkDO.setCity(clerkApplyDO.getCity());
                clerkDO.setExperience(clerkApplyDO.getExperience());
                clerkDO.setIntro(clerkApplyDO.getIntro());
                clerkDO.setWeixin(clerkApplyDO.getWeixin());
                clerkDO.setSound(clerkApplyDO.getSound());
                clerkDO.setSoundTime(clerkApplyDO.getSoundTime());
                clerkDO.setWorkerClerkLevelId(clerkLevelDO.getId());
                clerkDO.setLevel(clerkLevelDO.getLevel());
                clerkDO.setStatus(true);
                clerkMapper.updateById(clerkDO);
            }
        }

        clerkApplyDO.setStatus(status);
        clerkApplyMapper.updateById(clerkApplyDO);
    }

    @Async
    public void sendApplyResultMessage(ClerkApplyDO clerkApplyDO) {
        socialClientApi.sendWxaSubscribeMessage(new SocialWxaSubscribeMessageSendReqDTO()
                .setUserId(clerkApplyDO.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue())
                .setTemplateTitle(CLERK_APPLY_SUCCESS)
                .setPage("pages/tabbar/index") // 首页
                .addMessage("thing2", "用户资料认证") // 认证内容
                .addMessage("date3", LocalDateTimeUtil.formatNormal(clerkApplyDO.getCreateTime())) // 申请时间
                .addMessage("time9", DateUtil.now()) // 审核时间
                .addMessage("phrase1", "审核通过")); // 审核结果
    }

    @Override
    public List<CategoryRespVO> getGoodsList(Long id) {
        ClerkApplyDO clerkApplyDO = clerkApplyMapper.selectById(id);
        List<Long> goodsIdList = JSON.parseObject(clerkApplyDO.getGoodsIdArray(), new TypeReference<List<Long>>(){});
        // 商品缓存
        WorkerGoodsPageReqVO workerGoodsReqVO = new WorkerGoodsPageReqVO();
        List<WorkerGoodsDO> goodsList = workerGoodsMapper.selectList(workerGoodsReqVO);
        Map<Long, List<WorkerGoodsDO>> cateMap = new HashMap<>();
        Map<Long, List<WorkerGoodsDO>> cateParentMap = new HashMap<>();
        for(WorkerGoodsDO goods : goodsList){
            if(goodsIdList == null){
                goodsIdList = new ArrayList<>();
            }
            if(goodsIdList.contains(goods.getId())){
                goods.setDeleted(true);
            }else{
                goods.setDeleted(false);
            }
            List<WorkerGoodsDO> list = cateMap.get(goods.getCategoryId());
            if(list == null){
                list = new ArrayList<>();
                list.add(goods);
            }else{
                list.add(goods);
            }
            cateMap.put(goods.getCategoryId(), list);

            List<WorkerGoodsDO> parentList = cateParentMap.get(goods.getCategoryParentId());
            if(parentList == null){
                parentList = new ArrayList<>();
                parentList.add(goods);
            }else{
                parentList.add(goods);
            }
            cateParentMap.put(goods.getCategoryParentId(), parentList);
        }

        List<CategoryRespVO> dataList = new ArrayList<>();

        // 分类缓存
        Map<Long, List<CategoryDO>> categoryMap = new HashMap<>();
        CategoryPageReqVO categoryReqVO = new CategoryPageReqVO();
        List<CategoryDO> cateDOList = categoryMapper.selectList(categoryReqVO);
        for(CategoryDO cateDO : cateDOList){
            List<CategoryDO> list = categoryMap.get(cateDO.getParentId());
            if(list == null){
                list = new ArrayList<>();
                list.add(cateDO);
            }else{
                list.add(cateDO);
            }
            categoryMap.put(cateDO.getParentId(), list);
        }

        List<CategoryDO> cateList = categoryMap.get(-1L);
        if(!CollectionUtils.isEmpty(cateList)){
            for(CategoryDO cate : cateList){
                List<WorkerGoodsDO> list = cateMap.get(cate.getId());
                List<WorkerGoodsDO> parentList = cateParentMap.get(cate.getId());
                if(!CollectionUtils.isEmpty(list) || !CollectionUtils.isEmpty(parentList)){
                    CategoryRespVO category = new CategoryRespVO();
                    category.setId(cate.getId());
                    category.setName(cate.getName());
                    // 一级分类
                    if(!CollectionUtils.isEmpty(list)){
                        category.setGoodsList(list);
                    }
                    // 二级分类
                    if(!CollectionUtils.isEmpty(parentList)){
                        category.setRowspan(parentList.size());
                        List<CategoryDO> categoryDOList = categoryMap.get(cate.getId());
                        List<CategoryRespVO> categoryList = new ArrayList<>();
                        for(CategoryDO cateDO : categoryDOList){
                            if(!CollectionUtils.isEmpty(cateMap.get(cateDO.getId()))){
                                CategoryRespVO cateVO = new CategoryRespVO();
                                cateVO.setId(cateDO.getId());
                                cateVO.setName(cateDO.getName());
                                cateVO.setGoodsList(cateMap.get(cateDO.getId()));
                                categoryList.add(cateVO);
                            }
                        }
                        category.setCategoryList(categoryList);
                    }
                    dataList.add(category);
                }
            }
        }
        return dataList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGoodsList(AppClerkApplySaveReqVO updateReqVO) {
        ClerkApplyDO clerkApplyDO = clerkApplyMapper.getClerkApply(updateReqVO.getUserId(), updateReqVO.getId());
        List<CategoryRespVO> categoryList = updateReqVO.getCategoryList();
        Set<Long> goodsIdList = new HashSet<>();
        Set<Long> categoryIdList = new HashSet<>();
        for(CategoryRespVO category : categoryList){
            if(!CollectionUtils.isEmpty(category.getGoodsList())){
                for(WorkerGoodsDO goods : category.getGoodsList()){
                    if(goods.getDeleted()){
                        categoryIdList.add(category.getId());
                        goodsIdList.add(goods.getId());
                    }
                }
            }

            if(!CollectionUtils.isEmpty(category.getCategoryList())){
                for(CategoryRespVO cate : category.getCategoryList()){
                    if(!CollectionUtils.isEmpty(cate.getGoodsList())){
                        for(WorkerGoodsDO goods : cate.getGoodsList()){
                            if(goods.getDeleted()){
                                categoryIdList.add(category.getId());
                                categoryIdList.add(cate.getId());
                                goodsIdList.add(goods.getId());
                            }
                        }
                    }
                }
            }
        }

        clerkApplyDO.setGoodsIdArray(JSON.toJSONString(goodsIdList));
        clerkApplyDO.setCategoryIdArray(JSON.toJSONString(categoryIdList));
        clerkApplyMapper.updateById(clerkApplyDO);

        if(clerkApplyDO.getWorkerClerkId() != null){
            ClerkDO clerkDO = clerkMapper.selectById(clerkApplyDO.getWorkerClerkId());
            clerkDO.setGoodsIdArray(JSON.toJSONString(goodsIdList));
            clerkDO.setCategoryIdArray(JSON.toJSONString(categoryIdList));
            clerkMapper.updateById(clerkDO);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGoodsList(ClerkApplySaveReqVO updateReqVO) {
        ClerkApplyDO clerkApplyDO = clerkApplyMapper.getClerkApply(null, updateReqVO.getId());
        List<CategoryRespVO> categoryList = updateReqVO.getCategoryList();
        Set<Long> goodsIdList = new HashSet<>();
        Set<Long> categoryIdList = new HashSet<>();
        for(CategoryRespVO category : categoryList){
            if(!CollectionUtils.isEmpty(category.getGoodsList())){
                for(WorkerGoodsDO goods : category.getGoodsList()){
                    if(goods.getDeleted()){
                        categoryIdList.add(category.getId());
                        goodsIdList.add(goods.getId());
                    }
                }
            }

            if(!CollectionUtils.isEmpty(category.getCategoryList())){
                for(CategoryRespVO cate : category.getCategoryList()){
                    if(!CollectionUtils.isEmpty(cate.getGoodsList())){
                        for(WorkerGoodsDO goods : cate.getGoodsList()){
                            if(goods.getDeleted()){
                                categoryIdList.add(category.getId());
                                categoryIdList.add(cate.getId());
                                goodsIdList.add(goods.getId());
                            }
                        }
                    }
                }
            }
        }

        clerkApplyDO.setGoodsIdArray(JSON.toJSONString(goodsIdList));
        clerkApplyDO.setCategoryIdArray(JSON.toJSONString(categoryIdList));
        clerkApplyMapper.updateById(clerkApplyDO);

        if(clerkApplyDO.getWorkerClerkId() != null){
            ClerkDO clerkDO = clerkMapper.selectById(clerkApplyDO.getWorkerClerkId());
            clerkDO.setGoodsIdArray(JSON.toJSONString(goodsIdList));
            clerkDO.setCategoryIdArray(JSON.toJSONString(categoryIdList));
            clerkMapper.updateById(clerkDO);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOnlineStatus(AppClerkApplySaveReqVO updateReqVO) {
        ClerkApplyDO updateObj = clerkApplyMapper.getClerkApply(updateReqVO.getUserId(), updateReqVO.getId());
        updateObj.setBlindStatus(updateReqVO.getBlindStatus());
        updateObj.setOnlineStatus(updateReqVO.getOnlineStatus());
        updateObj.setOnlineTime(updateReqVO.getOnlineTime());
        clerkApplyMapper.updateById(updateObj);

        if(updateObj.getWorkerClerkId() != null){
            ClerkDO clerkDO = clerkMapper.selectById(updateObj.getWorkerClerkId());
            clerkDO.setBlindStatus(updateReqVO.getBlindStatus());
            clerkDO.setOnlineStatus(updateReqVO.getOnlineStatus());
            clerkDO.setOnlineTime(updateReqVO.getOnlineTime());
            clerkMapper.updateById(clerkDO);
        }
    }

    @Override
    public ClerkLevelDO getClerkLevel(Long id) {
        ClerkApplyDO clerkApplyDO = clerkApplyMapper.selectById(id);
        ClerkLevelDO clerkLevelDO = clerkLevelMapper.getClerkLevel();
        if(clerkApplyDO.getWorkerClerkId() != null){
            ClerkDO clerkDO = clerkMapper.selectById(clerkApplyDO.getWorkerClerkId());
            clerkLevelDO = clerkLevelMapper.selectById(clerkDO.getWorkerClerkLevelId());
        }
        return clerkLevelDO;
    }

    @Override
    public List<CategoryRespVO> getAppGoodsList(Long id) {
        ClerkApplyDO clerkApplyDO = clerkApplyMapper.selectById(id);
        List<Long> goodsIdList = JSON.parseObject(clerkApplyDO.getGoodsIdArray(), new TypeReference<List<Long>>(){});
        // 商品缓存
        AppWorkerGoodsPageReqVO workerGoodsReqVO = new AppWorkerGoodsPageReqVO();
        List<WorkerGoodsDO> goodsList = workerGoodsMapper.selectList(workerGoodsReqVO);
        Map<Long, List<WorkerGoodsDO>> cateMap = new HashMap<>();
        Map<Long, List<WorkerGoodsDO>> cateParentMap = new HashMap<>();
        for(WorkerGoodsDO goods : goodsList){
            if(goodsIdList == null){
                goodsIdList = new ArrayList<>();
            }
            if(goodsIdList.contains(goods.getId())){
                goods.setDeleted(true);
            }else{
                goods.setDeleted(false);
            }
            List<WorkerGoodsDO> list = cateMap.get(goods.getCategoryId());
            if(list == null){
                list = new ArrayList<>();
                list.add(goods);
            }else{
                list.add(goods);
            }
            cateMap.put(goods.getCategoryId(), list);

            List<WorkerGoodsDO> parentList = cateParentMap.get(goods.getCategoryParentId());
            if(parentList == null){
                parentList = new ArrayList<>();
                parentList.add(goods);
            }else{
                parentList.add(goods);
            }
            cateParentMap.put(goods.getCategoryParentId(), parentList);
        }

        List<CategoryRespVO> dataList = new ArrayList<>();

        // 分类缓存
        Map<Long, List<CategoryDO>> categoryMap = new HashMap<>();
        AppCategoryPageReqVO categoryReqVO = new AppCategoryPageReqVO();

        TradeConfigRespDTO config = tradeConfigApi.getTradeConfig();
        if(!CollectionUtils.isEmpty(config.getOpenCityAreas())){
            AddressRespDTO address = tradeConfigApi.getAddressByIp(getClientIP());
            categoryReqVO.setIsOline(config.getOpenCityAreas().contains(address.getProvince()));
        }

        List<CategoryDO> cateDOList = categoryMapper.selectList(categoryReqVO);
        for(CategoryDO cateDO : cateDOList){
            List<CategoryDO> list = categoryMap.get(cateDO.getParentId());
            if(list == null){
                list = new ArrayList<>();
                list.add(cateDO);
            }else{
                list.add(cateDO);
            }
            categoryMap.put(cateDO.getParentId(), list);
        }

        List<CategoryDO> cateList = categoryMap.get(-1L);
        if(!CollectionUtils.isEmpty(cateList)){
            for(CategoryDO cate : cateList){
                List<WorkerGoodsDO> list = cateMap.get(cate.getId());
                List<WorkerGoodsDO> parentList = cateParentMap.get(cate.getId());
                if(!CollectionUtils.isEmpty(list) || !CollectionUtils.isEmpty(parentList)){
                    CategoryRespVO category = new CategoryRespVO();
                    category.setId(cate.getId());
                    category.setName(cate.getName());
                    // 一级分类
                    if(!CollectionUtils.isEmpty(list)){
                        category.setGoodsList(list);
                    }
                    // 二级分类
                    if(!CollectionUtils.isEmpty(parentList)){
                        category.setRowspan(parentList.size());
                        List<CategoryDO> categoryDOList = categoryMap.get(cate.getId());
                        List<CategoryRespVO> categoryList = new ArrayList<>();
                        for(CategoryDO cateDO : categoryDOList){
                            if(!CollectionUtils.isEmpty(cateMap.get(cateDO.getId()))){
                                CategoryRespVO cateVO = new CategoryRespVO();
                                cateVO.setId(cateDO.getId());
                                cateVO.setName(cateDO.getName());
                                cateVO.setGoodsList(cateMap.get(cateDO.getId()));
                                categoryList.add(cateVO);
                            }
                        }
                        category.setCategoryList(categoryList);
                    }
                    dataList.add(category);
                }
            }
        }
        return dataList;
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private ClerkApplyServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}